package com.mbmed.doctor.commons.utils;

import java.io.IOException;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 表达式求值器
 * 一个简单的表达式求值器,可计算带括号的数学算式
 * 如 a + b*(10 - c)/12
 * 
 * 增强的eval2函数除了可用来计算数字表达式外,还可以用于对字符模板进行求值,如
 * "My name is #{name}"
 * 如果name的值为Tom,则返回的字符串为
 * My name is Tom
 * 注意:字符串表达式必须用双引号括起来,字符串中的转义字符同Java的一样,如双引号用\",换行为\n
 * @author yangzhengxin(Jason)
 *
 */
public class Evaluator {
	
	public static void main(String[] args){
		Map map = new HashMap();
		String str = "#{INCRE_FEE_02_201309}/(#{CUR_INCOME_201309}+#{INCRE_FEE_02_201307})";
//		String str = "(#{INCRE_FEE_02_201309}/3)";
		//( (to_number(a.STREAM_UP)/decode(to_number(a.STREAM),0,1)) >'50'  AND (to_number(a.STREAM_UP)/decode(to_number(a.STREAM),0,1)) <'500'   )

		map.put("INCRE_FEE_02_201308", "to_number(f5.INCRE_FEE_02)");
		map.put("INCRE_FEE_02_201309", "to_number(f6.INCRE_FEE_02)");
		map.put("CUR_INCOME_201308", "to_number(f3.CUR_INCOME)");
		map.put("CUR_INCOME_201307", "to_number(f1.CUR_INCOME)");
		map.put("INCRE_FEE_02_201307", "to_number(f4.INCRE_FEE_02)");
		map.put("CUR_INCOME_201309", "to_number(f2.CUR_INCOME)");
//		map.put("CCC", "to_number(a.CCC)");
//		map.put("DDD", "to_number(a.DDD)");
//		map.put("EEE", "to_number(a.EEE)");

//		System.out.println(evalString(str,map));
		
	}
	public static String eval2(String exp,Map params){
		if(exp==null)return null;
		return isStringExp(exp) ? evalString(exp,params) : String.valueOf(eval(exp,params));				
	}
	
	public static String evalString(String exp,Map params) {
		params=params==null ? Collections.EMPTY_MAP : params;
//		Pattern pattern=Pattern.compile("^\"(.*)\"$");
//		Matcher m=pattern.matcher(exp);
//		m.find();
//		exp=m.group(1);
		exp=exp.trim();
//		exp=exp.substring(1,exp.length() - 1);
		
		
		StringBuffer buf=new StringBuffer();
		for(int i=0;i<exp.length();i++){
			char c=exp.charAt(i);
			switch(c){
			case '\\':
				if(i+1<exp.length()){
					char nc=exp.charAt(i+1);
					switch(nc){
					case 'n':
						nc='\n';
						break;
					case 'r' :
						nc='\r';
						break;
					case 't':
						nc='\t';
						break;						
					}
					buf.append(nc);
					i++;
				}
				break;
			case '#' :
				String name=getVarName(exp,i);
				if(name==null){
					buf.append('#');
				}else{
					buf.append(params.get(name));
					i+=name.length()+2;
				}				
				break;
			 case '/' :
				buf.append(c);
				char cc=exp.charAt(i+1);
				if(cc=='('){
					//如果分母带括号，那就搜索另外一半括号，递归调用
					StringBuffer tmpBuf=new StringBuffer();
					String fenMu = "";
					buf.append("decode(");
//					tmpBuf.append(cc);
					for(int j=i+1;j<exp.length();j++){
						char tmpC=exp.charAt(j);
						if(tmpC==')') {
							tmpBuf.append(tmpC);
							fenMu = evalString(tmpBuf.toString(),params);
						}else {
							tmpBuf.append(tmpC);
						}
					}
					buf.append(fenMu).append(",0,00000000.1,")
					.append(fenMu).append(")");
					i+=fenMu.length();
//					String fenMu = evalString(exp.substring(i+1,exp.length()-1),params);
//					buf.append(cc).append("decode(").append(fenMu).append(",0,1)");
//					i+=fenMu.length()-1;
				}else if (cc=='#'){
					String fenMuName=getVarName(exp,i);
					if(fenMuName==null){
						buf.append('#');
					}else{
						buf.append("decode(").append(params.get(fenMuName)).append(",0,00000000.1,")
						   .append(params.get(fenMuName)).append(")");
						i+=fenMuName.length()+3;
					}				
				}else {
					i+=1;
					buf.append(cc);
				}
				break;
			 case ')' :
					buf.append(c);
					break;
			default :
				buf.append(c);
			}			
		}
		
		return buf.toString();
	}
	
	public static String verfiy(String exp){
		if(exp==null)return null;
		return isStringExp(exp) ? null : verfiyMathExp(exp);
	}
	
	private static String verfiyMathExp(String exp){
		int leftCount=exp.replaceAll("[^\\(]", "").length();
		int rightCount=exp.replaceAll("[^\\)]", "").length();
		if(leftCount>rightCount)return "缺少)括号";
		if(leftCount<rightCount)return "缺少(括号";
		return null;
	}
	
	
	private static String getVarName(String exp, int start) {
		Pattern pattern=Pattern.compile("#\\{(.*?)\\}");
		Matcher m=pattern.matcher(exp);
		if(!m.find(start))return null;
		return m.group(1);
	}

	/**
	 * 表达式求值
	 * @param exp 简单算式
	 * @return
	 */
	public static double eval(String exp){
		return eval(exp,null);
	}
	
	/**
	 * 表达式求值
	 * 
	 * @param exp 简单算式
	 * @param params 变量名-值Map,值可以是Number或数字字符串
	 * @return
	 */
	public static double eval(String exp,Map params){
		if(exp==null || "".equals(exp.trim()))return (double)0;
		params = params==null ? Collections.EMPTY_MAP : params;
		StringReader reader=new StringReader(exp);
		try {
			return doEval(reader,params);
		} catch (IOException e) {
			e.printStackTrace(); //IOException 不会发生
		}
		return 0;
	}
	
	
	public static Set getExpressionParams(String exp){
		return isStringExp(exp) ? getStringExpParams(exp) : getMathExpParams(exp);
	}
	
	private static Set getStringExpParams(String exp){
		Set result=new HashSet();
		Pattern pattern=Pattern.compile("#\\{(.*?)\\}");
		Matcher m=pattern.matcher(exp);
		while(m.find()){
			result.add(m.group(1));
		}
		return result;
		
	}

	private static Set getMathExpParams(String exp) {
		Set result=new HashSet();
		if(exp==null)return result;
		StreamTokenizer t= new StreamTokenizer(new StringReader(exp));
		t.ordinaryChar('/');
		t.ordinaryChar('-');
		t.ordinaryChar('*');
		t.ordinaryChar('+');
		
		try {
			int lastType=StreamTokenizer.TT_EOF;
			while(t.nextToken()!=StreamTokenizer.TT_EOF){
				if(t.ttype==StreamTokenizer.TT_WORD && !isNumber(lastType,t.sval)){
					result.add(t.sval);
				}
				lastType=t.ttype;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	static boolean isNumber(int lastType,String value){
		return lastType==StreamTokenizer.TT_NUMBER && 
		value.length()>1 &&
		(value.charAt(0)=='e' || value.charAt(0)=='E') && 
		((value.charAt(1)=='-' && value.length()>2 && value.charAt(2)>='0' && value.charAt(2)<='9') ||
		value.charAt(1)>='0' && value.charAt(1)<='9');
	}
	
	
	public static boolean isStringExp(String exp){
		if(exp==null)return false;
		exp=exp.replaceAll("\\n", " ");
		exp=exp.replaceAll("\\r", " ");
		exp=exp.replaceAll("\\t", " ");
		return exp.trim().matches("^\".*\"$");
	}	
	
	private static double doEval(Reader reader,Map params) throws IOException{
		StringBuffer buf=new StringBuffer();
		int c;
		while((c=reader.read())>=0){
			if(c=='('){
				buf.append(doEval(reader,params));
			}else if(c==')'){
				break;
			}else{
				buf.append((char)c);
			}
		}
		Evaluator evaluator=new Evaluator(buf.toString(),params);
		return evaluator.eval();
	}
	
	
	String expression;
	Map variables;
	
	Stack stack=new Stack();
	
	private Evaluator(String exp,Map variables){
		this.expression=exp;
		this.variables=variables;
	}
	
	double eval() {
		StreamTokenizer t= new StreamTokenizer(new StringReader(expression));
		t.ordinaryChar('/');
		t.ordinaryChar('-');
		t.ordinaryChar('*');
		t.ordinaryChar('+');
		Entry entry=null;
		while(true){
			try {
				t.nextToken();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			switch(t.ttype){
			case StreamTokenizer.TT_NUMBER:
				entry=new Entry();
				entry.value=t.nval;
				break;
			case StreamTokenizer.TT_WORD:
				if(entry!=null && (t.sval.charAt(0)=='e' || t.sval.charAt(0)=='E')){
					entry.operator='e';					
					stack.push(entry);
					if(t.sval.length()>1){
						entry=new Entry();
						entry.value=Double.parseDouble(t.sval.substring(1));
					}else{
						entry=null;
					}
				}else{
					entry=new Entry();
					entry.value=value(t.sval);
				}
				break;
			default :
				if(entry==null && t.ttype=='-'){
					entry=new Entry();
					entry.operator='g';
					entry.value=-1;
					stack.push(entry);
					entry=null;
				}else{
					if(t.ttype=='%'){
						entry.value=entry.value/100;
					}else{
						entry.operator=t.ttype;
						stack.push(entry);
						pullUp();
						entry=null;
						if(t.ttype==StreamTokenizer.TT_EOF){
							Entry result=stack.isEmpty() ? null : (Entry)stack.pop();
							return result==null ? (double)0 : result.value;
						}
					}
				}
			}	

		}
		
		
	}
	
	void pullUp(){
		if(stack.size()<2)return;
		Entry entry2=(Entry) stack.pop();
		Entry entry1=(Entry) stack.pop();
		if(getPri(entry1.operator) > getPri(entry2.operator)){
			stack.push(mergin(entry1,entry2));
			pullUp();
		}else{
			stack.push(entry1);
			stack.push(entry2);
		}
	}
	
	/**
	 * 合并操作单元
	 * @param entry1
	 * @param entry2
	 * @return
	 */
	Entry mergin(Entry entry1,Entry entry2){
		if(!stack.isEmpty()){
			Entry entry=(Entry) stack.peek();
			//从栈顶向栈底运算,需要作运算符变换,如 2 - 1 + 3 要变为 2-(1-3)
			if(entry.operator=='-' && entry1.operator=='-'){
				entry1.operator='+';
			}else if(entry.operator=='-' && entry1.operator=='+'){
				entry1.operator='-';
			}else if(entry.operator=='/' && entry1.operator=='/'){
				entry1.operator='*';
			}else if(entry.operator=='/' && entry1.operator=='*'){
				entry1.operator='/';
			}

		}
		//操作符运算
		double value=0;
		switch(entry1.operator){
		case '+':
			value=entry1.value + entry2.value;
			break;
		case '*' :
			value=entry1.value * entry2.value;
			break;
		case '-' :
			value=entry1.value - entry2.value;
			break;
		case '/' :
			value=entry1.value / entry2.value;
			break;	
		case 'g' :
			value=entry1.value * entry2.value;
			break;
		case 'e' :
			value=entry1.value * Math.pow(10, entry2.value);
		}
		entry1.operator=entry2.operator;
		entry1.value=value;
		
		return entry1;
	}
	
	/**
	 * 获得变量值
	 * @param name
	 * @return
	 */
	double value(String name){
		Object value= variables.get(name);
		if(value==null)return 0;
		double doubleValue=0;
		if(value instanceof Number){
			doubleValue=((Number)value).doubleValue();
		}else{
			doubleValue=Double.parseDouble(value.toString());
		}
		return doubleValue;
	}
	
	
	/**
	 * 获得操作符的优先级
	 * @param opt
	 * @return
	 */
	int getPri(int opt){
		switch(opt){
		case 'g':
			return 4;
		case 'e':
			return 3;			
		case '*':
		case '/':
			return 2;
		case '+':
		case '-':
			return 1;
		case StreamTokenizer.TT_EOF:
			return 0;
		}
		return 0;
	}
	
	
	/**
	 * 
	 * 操作单元
	 *
	 */
	class Entry{
		int operator; //操作符
		double value; //值
	}
	
	
	
	
}